package cn.foolishbird.crow.structure.tree;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 数据类型转换工具
 *
 * @author foolishbird
 */
public final class TreeUtils {

    private static final Logger log = LoggerFactory.getLogger(TreeUtils.class);

    private TreeUtils() {
    }

    /**
     * 将集合组装成树
     *
     * @param data  集合对象
     * @param apply 转换器
     * @param <T>   来源对象范型
     * @param <R>   目标对象范型
     * @return 目标对象树结构集合
     */
    public static <T extends TreeData, R> List<TreeNode<R>> convertTree(long topParentId, Collection<T> data,
                                                                        Function<T, R> apply) {
        // 入参校验
        if (CollectionUtils.isEmpty(data)) {
            return Collections.emptyList();
        }

        if (Objects.isNull(apply)) {
            throw new IllegalArgumentException("apply is not null");
        }

        // 数据过滤 + 转换 + 分组
        Map<Long, List<TreeNode<R>>> groupMap = data.stream()
                .filter(Objects::nonNull)
                .filter(d -> Objects.nonNull(d.id()))
                .filter(d -> Objects.nonNull(d.parentId()))
                .map(d -> TreeUtils.convertT(d, apply))
                .collect(Collectors.groupingBy(TreeNode::getParentId));

        List<TreeNode<R>> parents = groupMap.get(topParentId);
        if (CollectionUtils.isEmpty(parents)) {
            log.warn("list -> treeNode 数据源数据异常，没有顶级元素");
            return Collections.emptyList();
        }

        // 广度遍历填装
        List<TreeNode<R>> next = parents;
        while (!CollectionUtils.isEmpty(next)) {
            List<TreeNode<R>> temp = new ArrayList<>();
            next.forEach(n -> {
                List<TreeNode<R>> curChildren = groupMap.get(n.getId());
                if (!CollectionUtils.isEmpty(curChildren)) {
                    n.setChildren(curChildren);
                    temp.addAll(curChildren);
                }
            });
            next = temp;
        }
        return parents;
    }

    /**
     * 广度优先遍历，并且转换成想要的对象
     *
     * @param trees 树集合
     * @param apply 对象转换器
     * @param <T>   来源范型
     * @param <R>   目标范型
     * @return 目标对象集合
     */
    public static <T, R> List<R> bfsConverter(List<TreeNode<T>> trees, Function<TreeNode<T>, R> apply,
                                              Predicate<TreeNode<T>> test) {
        if (CollectionUtils.isEmpty(trees)) {
            return Collections.emptyList();
        }
        if (Objects.isNull(apply)) {
            throw new RuntimeException("转换对象不能为空");
        }
        if (Objects.isNull(test)) {
            throw new RuntimeException("校验对象不能为空");
        }
        List<TreeNode<T>> next = trees;
        List<R> converterList = Lists.newArrayList();
        while (!CollectionUtils.isEmpty(next)) {
            List<TreeNode<T>> temp = new ArrayList<>();
            next.forEach(n -> {
                R r = apply.apply(n);
                if (test.test(n)) {
                    converterList.add(r);
                }
                if (!CollectionUtils.isEmpty(n.getChildren())) {
                    temp.addAll(n.getChildren());
                }
            });
            next = temp;
        }
        return converterList;
    }

    /**
     * 类型转换
     *
     * @param t     来源对象
     * @param apply 转换器
     * @param <T>   来源对象范型
     * @param <R>   目标对象范型
     * @return 目标对象
     */
    public static <T extends TreeData, R> TreeNode<R> convertT(T t, Function<T, R> apply) {
        TreeNode<R> node = new TreeNode<>();
        node.setId(t.id());
        node.setParentId(t.parentId());
        node.setNode(apply.apply(t));
        return node;
    }

}
